package org.nf.trading.service.app.product.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nf.trading.dao.CommentMapper;
import org.nf.trading.dao.CommentReplyMapper;
import org.nf.trading.dao.ProductMapper;
import org.nf.trading.dto.CommentReplySecondaryDTO;
import org.nf.trading.entity.CommentReply;
import org.nf.trading.exception.app.product.FileUploadException;
import org.nf.trading.service.app.product.enums.ProductCommentEnum;
import org.nf.trading.service.es.EsService;
import org.nf.trading.service.es.document.product.ProductDoc;
import org.nf.trading.dto.ProductCommentDTO;
import org.nf.trading.entity.Comment;
import org.nf.trading.entity.Product;
import org.nf.trading.entity.User;
import org.nf.trading.exception.app.product.ProductDataException;
import org.nf.trading.service.app.product.ProductService;
import org.nf.trading.service.app.product.enums.ProductEnum;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class ProductServiceImpl implements ProductService {
    private final ProductMapper productMapper;

    private final CommentMapper commentMapper;

    private final CommentReplyMapper commentReplyMapper;

    private final RedisTemplate<String, Object> redisTemplate;

    private final EsService esService;

    private final MinioClient minioClient;

    /**
     * 在初始化后将商品信息存入到es
     */
    @PostConstruct
    public void initProductCache() {
        esService.initDoc(ProductDoc.class);
    }

    /**
     * 商品实体类转doc文档
     *
     * @param product
     * @return
     */
    private ProductDoc createProductDoc(Product product) {
        ProductDoc productDoc = new ProductDoc();
        productDoc.setProductId(product.getProductId());
        productDoc.setProductName(product.getProductName());
        productDoc.setProductDescription(product.getProductDescription());
        productDoc.setProductImg(product.getProductImg());
        productDoc.setProductPrice(product.getProductPrice());
        return productDoc;
    }

    /**
     * 通过用户id查询发布的商品
     *
     * @param uid
     * @return
     */
    @Override
    public List<Product> listProductsByUid(Integer uid) {
        if (uid == null) {
            throw new ProductDataException(500, "用户id不能为空");
        }
        return productMapper.listProductsByUid(uid);
    }

    /**
     * 文件上传
     *
     * @param path  桶下面的子目录
     * @param files
     * @return
     */
    @Override
    public boolean fileUpload(String path, MultipartFile[] files, Product product) {
        List<String> list = new ArrayList<>();
        try {
            //循环遍历上传的附件
            for (MultipartFile file : files) {
                //获取文件名
                String fileName = file.getOriginalFilename();
                //保存文件名
                list.add(fileName);
                //获取文件的输入流读取文件内容
                InputStream inputStream = file.getInputStream();
                //将文件上传到minio服务器
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket("mytrading")
                        //远程上传的路径
                        .object(path + "/" + fileName)
                        //设置一个输入流，-1表示读到文件的末尾
                        .stream(inputStream, file.getSize(), -1)
                        //文件的类型
                        .contentType(file.getContentType())
                        .build());
            }
            //图片第一张作为商品图片
            product.setProductImg(list.get(0));
            String description = product.getProductDescription();
            //设置商品名称
            if (description.length() > 24) {
                product.setProductName(description.substring(0, 24));
            } else {
                product.setProductName(description);
            }
            //序列化 设置描述图片
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(list);
            product.setProductDescriptionImg(json);
        } catch (Exception e) {
            log.error("文件上传失败,错误信息为：{}", e.getMessage());
            throw new FileUploadException(500, e.getMessage());
        }
        return true;
    }

    /**
     * 通过商品状态查询全部商品
     *
     * @param status
     * @return
     */
    @Override
    public List<Product> getProductByStatus(Integer status) {
        if (status == null) {
            log.error("status值为空");
            throw new ProductDataException(500, "status值为空");
        }
        return productMapper.getProductByStatus(status);
    }

    /**
     * 通过商品分类详细id查询全部商品
     *
     * @param productTypeDetailId
     * @return
     */
    @Override
    public List<Product> getProductByTypeDetailId(Integer productTypeDetailId) {
        if (productTypeDetailId == null) {
            log.error("productTypeDetailId值为空");
            throw new ProductDataException(500, "productTypeDetailId值为空");
        }
        return productMapper.getProductByTypeDetailId(productTypeDetailId);
    }

    /**
     * 分页查询
     *
     * @param pageNum  第几页
     * @param pageSize 显示数量
     * @return
     */
    @Override
    public PageInfo<Product> selectAll(Integer pageNum, Integer pageSize) {
        if (pageNum == null || pageSize == null) {
            throw new ProductDataException(500, "参数：pageNum或者pageSize为空");
        }
        PageHelper.startPage(pageNum, pageSize);
        List<Product> products = productMapper.selectAll();
        PageInfo<Product> pageInfo = new PageInfo<>(products);
        return pageInfo;
    }

    /**
     * 通过名称查询
     *
     * @param productName
     * @return
     */
    @Override
    public List<ProductDoc> getProductByName(String productName, Integer pageNum, Integer pageSize) {
        List<ProductDoc> productDocList = esService
                .boolSearch(ProductDoc.class, productName,
                        pageNum, pageSize,
                        "productName", "productDescription");
        return productDocList;
    }

    /**
     * 发布商品  后期使用mq
     *
     * @param product
     * @return
     */
    public boolean saveProduct(Product product, MultipartFile[] files) {
        if (files == null || product == null) {
            log.info("数据为空,发布商品失败");
            throw new ProductDataException(500, "发布商品失败");
        }
        boolean fileUpload = fileUpload("product", files, product);
        //判断文件是否上传成功
        if (!fileUpload) {
            throw new FileUploadException(500, "文件上传失败");
        }
        boolean result = productMapper.insert(product) == 1;
        //判断是否添加成功，成功则更新es
        if (result) {
            //更新es
            esService.createDoc(createProductDoc(product));
            return true;
        }
        return fileUpload;
    }
    /**
     * 通过id查询商品(包含发布者信息)
     *
     * @param productId
     * @return
     */
    @Override
    public ProductCommentDTO getProductById(Integer productId) {
        if (productId == null) {
            log.error("异常信息：id为空");
            throw new ProductDataException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "id为空");
        }
        ProductCommentDTO productCommentDTO = new ProductCommentDTO();
        //判断缓存中是否存在
        if (Boolean.FALSE.equals(redisTemplate.hasKey(ProductEnum.PREFIX.value() + productId))) {
            //获取商品信息
            Product product = productMapper.selectByPrimaryKey(productId);
            //获取评论
            List<Comment> commentList = commentMapper.getCommentsByProductId(productId);
            //获取评论回复中的user（评论者）
            commentList.forEach((comment -> {
                comment.getCommentReplies().forEach(commentReply -> {
                    Integer commentId = commentReply.getCommentId();
                    User user = commentReplyMapper.getCommentReplyUserById(commentId).getUser();
                    commentReply.setUser(user);
                });
            }));
            //重新放到缓存中
            redisTemplate.opsForValue().set(ProductEnum.PREFIX.value() + productId, product);
            redisTemplate.expire(ProductEnum.PREFIX.value() + productId, 10, TimeUnit.MINUTES);

            redisTemplate.opsForValue().set(ProductCommentEnum.PREFIX.value() + productId, commentList);
            redisTemplate.expire(ProductCommentEnum.PREFIX.value() + productId, 10, TimeUnit.MINUTES);
        }
        productCommentDTO.setProduct((Product)redisTemplate.opsForValue().get(ProductEnum.PREFIX.value() + productId));
        productCommentDTO.setCommentList((List<Comment>)redisTemplate.opsForValue().get(ProductCommentEnum.PREFIX.value() + productId));
        log.info("缓存中获取");
        return productCommentDTO;
    }

    /**
     * 通过id查询商品(包含发布者信息和回复级别) ：改良上面的业务
     *
     * @param productId
     * @return
     */
    @Override
    public ProductCommentDTO secondLevelComments(Integer productId) {
        if (productId == null) {
            log.error("异常信息：id为空");
            throw new ProductDataException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "id为空");
        }
        ProductCommentDTO productCommentDTO = new ProductCommentDTO();
        //判断缓存中是否存在商品
        if (Boolean.FALSE.equals(redisTemplate.hasKey(ProductEnum.PREFIX.value() + productId))) {
            //获取商品信息
            Product product = productMapper.selectByPrimaryKey(productId);
            productCommentDTO.setProduct(product);
            //重新放到缓存中
            redisTemplate.opsForValue().set(ProductEnum.PREFIX.value() + productId, product);
            redisTemplate.expire(ProductEnum.PREFIX.value() + productId, 30, TimeUnit.MINUTES);
        }
        //判断缓存中是否存在评论
        if (Boolean.FALSE.equals(redisTemplate.hasKey(ProductCommentEnum.PREFIX.value() + productId))) {
            //数据库查询商品评论
            List<Comment> commentList = commentMapper.getCommentsByProductId(productId);
            encapsulationCommentReplySecondary(commentList, productId);
            //重新放到缓存中
            redisTemplate.opsForValue().set(ProductCommentEnum.PREFIX.value() + productId, commentList);
            redisTemplate.expire(ProductCommentEnum.PREFIX.value() + productId, 30, TimeUnit.MINUTES);
        }
        //缓存中获取商品信息
        productCommentDTO.setProduct((Product) redisTemplate.opsForValue().get(ProductEnum.PREFIX.value() + productId));
        //缓存中获取商品评论
        productCommentDTO.setCommentList((List<Comment>) redisTemplate.opsForValue()
                .get(ProductCommentEnum.PREFIX.value() + productId));
//
//        //获取评论，判断缓存中是否存在
//        if (Boolean.FALSE.equals(redisTemplate.hasKey(ProductCommentEnum.PREFIX.value() + productId))){
//            commentList = commentMapper.getCommentsByProductId(productId);
//            //封装评论回复信息
//            encapsulationCommentReplySecondary(commentList,productId);
//        }else {
//            //获取缓存中的评论
//            commentList = (List<Comment>) redisTemplate.opsForValue().get(ProductCommentEnum.PREFIX.value() + productId);
//            System.out.println("从缓存中获取评论");
//        }
        //保存到商品dto中
//        ProductCommentDTO productCommentDTO = new ProductCommentDTO(product, commentList);
        //重新放到缓存中
//        redisTemplate.opsForValue().set(ProductEnum.PREFIX.value() + productId, productCommentDTO);
//        redisTemplate.expire(ProductEnum.PREFIX.value() + productId,10, TimeUnit.MINUTES);
        log.info("从缓存中获取商品信息");
        return productCommentDTO;
    }

    @Override
    public PageInfo<Product> selectAllProduct(Integer pageNum, Integer pageSize, Product product) {
        return new PageInfo<>(productMapper.selectAllProduct(pageNum,pageSize,product));
    }

    @Override
    public AtomicLong productCommentNum(Integer pid) {
        // 先查询评论信息
        AtomicLong commentNum = new AtomicLong(commentMapper.countComment(pid));
        List<Comment> comment = commentMapper.getCommentByProductId(pid);
        comment.forEach(c -> commentNum.addAndGet(commentReplyMapper.countCommentReply(c.getCommentId())));

        return commentNum;
    }

    /**
     * 封装评论
     *
     * @param commentList
     */
    private void encapsulationCommentReplySecondary(List<Comment> commentList, Integer productId) {
        //获取评论回复中的user（评论者）
        commentList.forEach((comment -> {
            //定义数组
            List<CommentReplySecondaryDTO> commentReplySecondaryDTOList = new ArrayList<>();
            List<CommentReply> commentReplies = commentReplyMapper.listAllByCommentId(comment.getCommentId());
            commentReplies.forEach(commentReply -> {
                //创建dto保存
                CommentReplySecondaryDTO secondaryDTO = new CommentReplySecondaryDTO();
                //拿到评论回复的id
                Integer commentReplyId = commentReply.getCommentReplyId();
                //先拿到评论的回复
                CommentReply commentReplyUserById = commentReplyMapper.getCommentReplyUserById(commentReplyId);
                //拿到评论回复的评论id
                Integer secondary = commentReply.getCommentReplySecondary();
                CommentReply commentReplyById = commentReplyMapper.getCommentReplyUserById(secondary);
                //判断评论回复是否为空
                if (secondary == null) {
                    //保存回复的用户
                    secondaryDTO.setCommentator(null);
                } else {
                    //保存回复的用户
                    secondaryDTO.setCommentator(commentReplyById.getUser());
                }
                //保存评论者
                secondaryDTO.setUser(commentReplyUserById.getUser());
                secondaryDTO.setCommentReplyId(commentReplyUserById.getCommentReplyId());
                //保存评论信息
                secondaryDTO.setContent(commentReply.getCommentReplyContent());

                //保存到集合中
                commentReplySecondaryDTOList.add(secondaryDTO);
            });
            System.out.println("Arrays.toString" + Arrays.toString(commentReplySecondaryDTOList.toArray()));
            comment.setSecondaryDTO(commentReplySecondaryDTOList);
        }));
        redisTemplate.opsForValue().set(ProductCommentEnum.PREFIX.value() + productId, commentList);
        redisTemplate.expire(ProductCommentEnum.PREFIX.value() + productId, 20, TimeUnit.MINUTES);
    }


    /**
     * 测试回复评论
     *
     * @param productId
     * @return
     */
    public ProductCommentDTO getProductById2(Integer productId) {
        if (productId == null) {
            log.error("异常信息：id为空");
            throw new ProductDataException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "id为空");
        }
        //判断缓存中是否存在
        if (Boolean.FALSE.equals(redisTemplate.hasKey(ProductEnum.PREFIX.value() + productId))) {
            System.out.println("缓存中不存在");
            //获取商品信息
            Product product = productMapper.selectByPrimaryKey(productId);
            //获取评论
            List<Comment> commentList = commentMapper.getCommentsByProductId(productId);
            //通过评论id查询回复
//            commentList.forEach(comment -> {
//                List<CommentReply> commentReplies = commentReplyMapper.listAllByCommentId(comment.getCommentId());
//                comment.setCommentReplies(commentReplies);
//            });
            List<CommentReplySecondaryDTO> commentReplySecondaryDTOList = new ArrayList<>();
            //获取评论回复中的user（评论者）
            commentList.forEach((comment -> {
                System.out.println("值是" + comment);
                List<CommentReply> commentReplies = commentReplyMapper.listAllByCommentId(comment.getCommentId());
                commentReplies.forEach(commentReply -> {
                    //拿到评论回复的id
                    Integer commentReplyId = commentReply.getCommentReplyId();
                    //拿到评论回复的评论id
                    Integer secondary = commentReply.getCommentReplySecondary();
                    //先拿到评论的回复
                    User user = commentReplyMapper.getCommentReplyUserById(commentReplyId).getUser();
                    System.out.println("secondary = " + secondary);
                    //再拿到评论回复的评论
//                    User secondaryUser = commentReplyMapper.getCommentReplyUserById(secondary);
                    CommentReply commentReplyById = commentReplyMapper.getCommentReplyUserById(secondary);
//                    System.out.println("secondaryUser=" + secondaryUser);
//                    commentReply.setUser(user);
                    CommentReplySecondaryDTO secondaryDTO = new CommentReplySecondaryDTO();
                    secondaryDTO.setUser(user);
                    //设置评论
                    secondaryDTO.setContent(commentReply.getCommentReplyContent());
                    secondaryDTO.setCommentator(commentReplyById.getUser());
                    secondaryDTO.setCommentReplyId(commentReplyById.getCommentReplyId());

                    commentReplySecondaryDTOList.add(secondaryDTO);
                });
                comment.setSecondaryDTO(commentReplySecondaryDTOList);
            }));
            ProductCommentDTO productCommentDTO = new ProductCommentDTO(product, commentList);
            //重新放到缓存中
            redisTemplate.opsForValue().set(ProductEnum.PREFIX.value() + productId, productCommentDTO);
            redisTemplate.expire(ProductEnum.PREFIX.value() + productId, 10, TimeUnit.MINUTES);
        }
        log.info("缓存中获取");
        return (ProductCommentDTO) redisTemplate.opsForValue().get(ProductEnum.PREFIX.value() + productId);
    }

}
